package com.wk.net.heartbeat.handler;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class HeartbeatObserver {
	/**
	 * 创建HeartbeatAbsenceDetecter，用于根据心跳信号判断是否在线
	 * 
	 * @param absenceTolerance
	 *            掉线容忍度。任一对象的代数到达该值则判断它掉线<br>
	 *            所有对象的初始代数为0<br>
	 *            在一个周期内，如果收到某个对象心跳，则其代数归零；否则代数+1
	 */
	public HeartbeatObserver(int absenceTolerance) {
		this.absenceTolerance = absenceTolerance;

		observedObjects = new HashMap<>();

		newlyAbsent = new HashSet<>();
		newlyPresent = new HashSet<>();

		lastAbsent = new HashSet<>();
		lastPresent = new HashSet<>();
	}

	/**
	 * 监视一个对象的状态<br>
	 * 如果初始状态为在线，则其代数为0；否则为absenceTolerance，代表初始时已经掉线
	 * 
	 * @param name
	 *            对象名称
	 * @param isOnline
	 *            该对象当前是否在线
	 */
	public void observe(String name, boolean isOnline) {
		if (isOnline) {
			observedObjects.put(name, 0);
		} else {
			observedObjects.put(name, absenceTolerance);
		}
	}

	/**
	 * 不再监视对象的状态
	 * 
	 * @param name
	 */
	public void ignore(String name) {
		observedObjects.remove(name);
	}

	public boolean isObserving(String name) {
		return observedObjects.containsKey(name);
	}

	/**
	 * 记录收到了某个对象的心跳
	 * 
	 * @param name 对象名称
	 */
	public void onHeartbeat(String name) {
		if (observedObjects.containsKey(name)) {
			if (observedObjects.get(name) >= absenceTolerance) {
				newlyPresent.add(name);
			}
			observedObjects.put(name, -1);
		}
	}

	/**
	 * 结算一个检查点，判定受监测的对象的状态<br>
	 * 之后可调用getNewlyAbsent()、getNewlyPresent查看改变了状态的对象
	 */
	public void check() {
		for (Map.Entry<String, Integer> object : observedObjects.entrySet()) {
			object.setValue(object.getValue() + 1);
			if (object.getValue() == absenceTolerance) {
				newlyAbsent.add(object.getKey());
			}
		}

		migratePresenceRecord();
	}

	/**
	 * @return 在最近的检查点处，从在线变为掉线的对象
	 */
	public Set<String> getRecentlyAbsent() {
		return lastAbsent;
	}

	/**
	 * @return 在最近的检查点处，从掉线变为在线的对象
	 * @return
	 */
	public Set<String> getRecentlyPresent() {
		return lastPresent;
	}

	private void migratePresenceRecord() {
		lastAbsent.clear();
		lastAbsent.addAll(newlyAbsent);
		newlyAbsent.clear();

		lastPresent.clear();
		lastPresent.addAll(newlyPresent);
		newlyPresent.clear();
	}

	private int absenceTolerance;

	// Key：被监视的对象；Value：代数（generation）
	private Map<String, Integer> observedObjects;

	private Set<String> newlyAbsent;
	private Set<String> newlyPresent;

	private Set<String> lastAbsent;
	private Set<String> lastPresent;
}
